package org.kordakus.shoot.model;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;

import org.apache.log4j.Logger;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.Sanselan;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.formats.jpeg.JpegImageMetadata;
import org.apache.sanselan.formats.jpeg.exifRewrite.ExifRewriter;
import org.apache.sanselan.formats.tiff.TiffField;
import org.apache.sanselan.formats.tiff.TiffImageMetadata;
import org.apache.sanselan.formats.tiff.constants.ExifTagConstants;
import org.apache.sanselan.formats.tiff.constants.TiffConstants;
import org.apache.sanselan.formats.tiff.write.TiffOutputDirectory;
import org.apache.sanselan.formats.tiff.write.TiffOutputField;
import org.apache.sanselan.formats.tiff.write.TiffOutputSet;
import org.joda.time.DateTime;
import org.kordakus.shoot.model.util.ExifFormatUtil;

public class ImageFile implements Serializable
{

	private static final long	serialVersionUID	= 5717928954870786472L;

	private static final Logger	_logger				= Logger.getLogger(ImageFile.class);

	private final File			_image;

	private String				_cameraModel;
	private String				_cameraMake;
	private DateTime			_dateCreated		= null;

	public ImageFile(File file) throws IOException
	{
		this._image = file;
		initializeObject();
	}

	public File getFile()
	{
		return _image;
	}

	private void initializeObject() throws IOException
	{
		readImage();
	}

	public String getCameraModel()
	{
		return _cameraModel;
	}

	public String getCameraMake()
	{
		return _cameraMake;
	}

	public DateTime getDateCreated()
	{
		return _dateCreated;
	}

	public void setCameraModel(String model)
	{
		if (model != null)
		{
			_logger.debug("Setting new camera model value (value=" + model
					+ ")");
			_cameraModel = model;
		}
	}

	public void setCameraMake(String make)
	{
		if (make != null)
		{
			_logger.debug("Setting new camera make value (value=" + make + ")");
			_cameraMake = make;
		}
	}

	public void setDateCreated(DateTime dateCreated)
	{
		if (dateCreated != null)
		{
			_dateCreated = dateCreated;
		}
	}

	/**
	 * 
	 * @throws IOException
	 */
	public void readImage() throws IOException
	{
		_logger.info("Reading file '" + _image.getAbsolutePath() + "'...");
		InputStream stream = null;
		try
		{
			stream = new FileInputStream(_image);

			IImageMetadata data = Sanselan.getMetadata(stream,
					_image.getAbsolutePath());
			if (data instanceof JpegImageMetadata)
			{
				JpegImageMetadata meta = (JpegImageMetadata) data;

				//
				//
				// DATE TIME ORIGINAL

				TiffField createdField = meta
						.findEXIFValue(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
				if (createdField != null)
				{
					_dateCreated = ExifFormatUtil.parseExifDate(createdField
							.getStringValue().trim());
					_logger.debug("Reading file... tag date time original found (value="
							+ _dateCreated + ")");
				} else
				{
					_logger.warn("Reading file... tag date time original not found");
				}

				//
				//
				// CAMERA MAKE

				TiffField makerField = meta
						.findEXIFValue(ExifTagConstants.EXIF_TAG_MAKE);
				if (makerField != null)
				{
					_cameraMake = makerField.getStringValue().trim();
					_logger.debug("Reading file... tag camera make found (value="
							+ _cameraMake + ")");
				} else
				{
					_logger.warn("Reading file... tag camera make not found");
				}

				//
				//
				// CAMERA MODEL

				TiffField modelField = meta
						.findEXIFValue(ExifTagConstants.EXIF_TAG_MODEL);
				if (modelField != null)
				{
					_cameraModel = modelField.getStringValue().trim();
					_logger.debug("Reading file... tag camera model found (value="
							+ _cameraModel + ")");
				} else
				{
					_logger.warn("Reading file... tag camera model not found");
				}
			}
		} catch (Exception e)
		{
			_logger.error("Reading file '" + _image.getAbsolutePath()
					+ "'... failed (" + e.getMessage() + ")");
			_logger.debug("Reading file '" + _image.getAbsolutePath()
					+ "'... failed", e);
			throw new IOException(e.getMessage(), e);
		} finally
		{
			if (stream != null)
			{
				stream.close();
			}
		}
	}

	public boolean writeImage(OutputStream stream)
	{
		_logger.info("Writing image to stream...");
		try
		{
			TiffOutputSet outputSet = null;

			JpegImageMetadata metadata = (JpegImageMetadata) Sanselan
					.getMetadata(_image);

			if (metadata != null)
			{
				TiffImageMetadata asd = metadata.getExif();
				outputSet = asd.getOutputSet();

				if (outputSet == null)
				{
					outputSet = new TiffOutputSet();
				}

				TiffOutputDirectory exifDir = outputSet
						.getOrCreateExifDirectory();

				String data = ExifFormatUtil.formatExifDate(_dateCreated);
				TiffOutputField datetaken = new TiffOutputField(
						TiffConstants.EXIF_TAG_DATE_TIME_ORIGINAL,
						TiffConstants.FIELD_TYPE_ASCII, data.length(),
						data.getBytes());
				exifDir.removeField(TiffConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
				exifDir.add(datetaken);

				new ExifRewriter().updateExifMetadataLossless(_image, stream,
						outputSet);
				stream.flush();
			}

			_logger.info("Writing new exif information on image... done");
			return true;
		} catch (ImageReadException e)
		{
			_logger.info("Writing new exif information on image... failed ("
					+ e.getMessage() + ")");
			_logger.debug("Writing new exif information on image... failed", e);
		} catch (ImageWriteException e)
		{
			_logger.info("Writing new exif information on image... failed ("
					+ e.getMessage() + ")");
			_logger.debug("Writing new exif information on image... failed", e);
		} catch (IOException e)
		{
			_logger.info("Writing new exif information on image... failed ("
					+ e.getMessage() + ")");
			_logger.debug("Writing new exif information on image... failed", e);
		} finally
		{
			try
			{
				_logger.debug("Closing stream...");
				stream.close();
			} catch (IOException e)
			{
				_logger.warn("Failed to release image file", e);
			}
		}

		return false;
	}

}
